using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.IO;
using System.Diagnostics;

namespace TALoaders
{
    public enum QuoteType
    {
        Open,
        High,
        Low,
        Close,
        Volume,
        AdjClose,
        AvgHL,  // AvgHL = (High + Low)/2 
        AvgHLC  // AvgHLC= (High + Low + Close)/3
    }

    /// <summary>Encapsulates daily quote values open, high, low, close, volume, adjClose</summary>
    public struct Quote
    {
        private Dictionary<QuoteType, double> valueM;

        #region Public Methods
        public Quote(double open, double high, double low, double close, double volume, double adjClose)
        {
            this.valueM = new Dictionary<QuoteType, double>(6);
            this.valueM.Add(QuoteType.Open, open);
            this.valueM.Add(QuoteType.High, high);
            this.valueM.Add(QuoteType.Low, low);
            this.valueM.Add(QuoteType.Close, close);
            this.valueM.Add(QuoteType.Volume, volume);
            this.valueM.Add(QuoteType.AdjClose, adjClose);
            // calculate derived quotes
            double avgHL = 0.5 * (high + low);
            this.valueM.Add(QuoteType.AvgHL, avgHL);

            double avgHLC = (high + low + close) / 3.0;
            this.valueM.Add(QuoteType.AvgHLC, avgHLC);

        }
        public double getValue(QuoteType type)
        {
            double result;
            if (!this.valueM.TryGetValue(type, out result))
                throw new Exception("Unknown QuoteType: " + type.ToString());
            return result;
        }
        public double Open
        {
            get { return this.getValue(QuoteType.Open); }
        }
        public double High
        {
            get { return this.getValue(QuoteType.High); }
        }
        public double Low
        {
            get { return this.getValue(QuoteType.Low); }
        }
        public double Close
        {
            get { return this.getValue(QuoteType.Close); }
        }
        public double Volume
        {
            get { return this.getValue(QuoteType.Volume); }
        }
        public override string ToString()
        {
            StringBuilder s = new StringBuilder();
            foreach (KeyValuePair<QuoteType, double> typeValue in valueM)
                s.Append(typeValue.Key.ToString() + ":" + typeValue.Value.ToString() + ", ");
            return s.ToString();
        }
        #endregion Public Methods
    }

    public class Utilities
    {
        static public SortedList<DateTime, double> getTimeSeries(SortedList<DateTime, Quote> quotes, QuoteType type)
        {
            SortedList<DateTime, double> result = new SortedList<DateTime, double>(quotes.Count);
            foreach (KeyValuePair<DateTime, Quote> dateQuotePair in quotes)
                result.Add(dateQuotePair.Key, dateQuotePair.Value.getValue(type));
            return result;
        }

        /// <summary>Save existing DataTable into CSV file</summary>
        /// <param name="fileName">Name of the file to save to</param>
        /// <param name="dTable">Source Datatable to save to the file</param>
        static public void saveDataTableToCSV(string fileName, DataTable dTable)
        {
            StringBuilder sb = new StringBuilder();
            int nOfCols = dTable.Columns.Count;
            // create Header Row
            for (int colIx = 0; colIx < nOfCols; ++colIx)
            {
                if (colIx > 0)
                    sb.Append(",");
                sb.Append(dTable.Columns[colIx].ColumnName);
            }
            sb.AppendLine();
            // Iterate over rows
            foreach (DataRow row in dTable.Rows)
            {
                for (int colIx = 0; colIx < nOfCols; ++colIx)
                {
                    if (colIx > 0)
                        sb.Append(",");
                    sb.Append(row[colIx].ToString());
                }
                sb.AppendLine();
            }
            File.WriteAllText(fileName, sb.ToString());
        }

        /// <summary>Load (already existing) DtaaTable form a CSV file</summary>
        /// <param name="fileName">name of the file to load from</param>
        /// <param name="dTable">DataTable to populate from the file</param>
        /// <remarks>The DataTable has to have columns already set-up</remarks>
        static public void loadCSVToDataTable(string fileName, ref DataTable dTable)
        {
            string[] lines = File.ReadAllText(fileName).Split('\n');
            string[] header = new string[0];
            bool isFirstLine = true;
            foreach (string line in lines)
            {
                string[] values = line.Trim('\r').Split(',');
                if (isFirstLine)
                {
                    header = (string[])values.Clone();
                    for (int colIx = 0; colIx < header.Length; ++colIx)
                        if (header[colIx] != dTable.Columns[colIx].ColumnName)
                            throw new Exception("Column names missmatch. ColIx=" + colIx + " in file " + fileName + " does not has name " + header[colIx] + ", expected " + dTable.Columns[colIx].ColumnName);
                    isFirstLine = false;
                }
                else
                {
                    if (values.Length != header.Length)
                        continue;
                    DataRow row = dTable.NewRow();
                    for (int colIx = 0; colIx < values.Length; ++colIx)
                    {
                        Type t = dTable.Columns[colIx].DataType;
                        if (t == typeof(double))
                            row[header[colIx]] = Convert.ToDouble(values[colIx]);
                        else if (t == typeof(DateTime))
                            row[header[colIx]] = Convert.ToDateTime(values[colIx]);
                        else if (t == typeof(int))
                            row[header[colIx]] = Convert.ToInt32(values[colIx]);
                        else if (t == typeof(Loader))
                            row[header[colIx]] = (Loader)Enum.Parse(typeof(Loader), values[colIx]);
                        else if (t == typeof(string))
                            row[header[colIx]] = values[colIx];
                        else
                            throw new Exception("Unsupported type: " + t + ". colIx=" + colIx + ", header=" + header[colIx] + ", value=" + values[colIx]);
                    }
                    dTable.Rows.Add(row);
                }
            }
        }

        /// <summary> Make usure that date is on the week day (not on Saturday or Sunday). Return it back if weekday. Otherwise return the next Monday</summary>
        /// <param name="date"></param>
        /// <returns>If date is week-day return it back. If it is weekend then return the next Modnay</returns>
        static public DateTime thisOrNextWeekDay(DateTime date)
        {
            if (date.DayOfWeek == DayOfWeek.Sunday)
                date = date.AddDays(1);
            else if (date.DayOfWeek == DayOfWeek.Saturday)
                date = date.AddDays(2);
            return date;
        }

        /// <summary>Make sure that date is week day. Return it back if weekday. Otherwise return last Friday</summary>
        /// <param name="date"></param>
        /// <returns>If date is week-day return it back. If it is weekend day the return the previous Friday</returns>
        static public DateTime thisOrPrevWeekDay(DateTime date)
        {
            if (date.DayOfWeek == DayOfWeek.Sunday)
                date = date.AddDays(-2);
            else if (date.DayOfWeek == DayOfWeek.Saturday)
                date = date.AddDays(-1);
            return date;
        }
    }

}
